#pragma once

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "../comm/log.hpp"

// 只负责代码的编译

namespace ns_runner
{
    using namespace ns_log;
    using namespace ns_util;

    class Runner
    {
    public:
        /*
        限制进程的cpu运行时间和内存大小
        单位 ：cpu--秒 ，内存--KB
        */
        static void SetProLimit(int cpu_time, int mem_size)
        {
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            cpu_rlimit.rlim_cur = cpu_time;
            setrlimit(RLIMIT_CPU, &cpu_rlimit);

            struct rlimit mem_rlimit;
            mem_rlimit.rlim_max = RLIM_INFINITY;
            // 系统接口的单位是字节,我们向上层提供的单位是KB
            mem_rlimit.rlim_cur = mem_size * 1024;
            setrlimit(RLIMIT_AS, &mem_rlimit);
        }

        /*
        返回值 > 0 ：程序异常，收到了信号，返回值就是信号编号
        返回值 == 0 ：正常运行完毕，结果保存到临时文件中
        返回值 < 0 : 内部错误
        参数：文件名、cpu秒数、内存（KB）
        */
        static int Run(const std::string &file_name, int cpu_time, int mem_size)
        {
            pid_t pid = fork();

            // 添加路径名
            std::string _excute = PathUtil::Exe(file_name);
            std::string _stdin = PathUtil::Stdin(file_name);
            std::string _stdout = PathUtil::Stdout(file_name);
            std::string _stderr = PathUtil::Stderr(file_name);

            // 打开文件
            umask(0);
            int _stdin_fd = open(_stdin.c_str(), O_CREAT | O_RDONLY, 0644);
            int _stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);
            int _stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);

            if (_stdin_fd < 0 && _stdout_fd < 0 && _stderr_fd < 0)
            {
                LOG(ERROR) << "运行时打开标准文件失败" << std::endl;
                return -1; // 表示打开文件失败
            }

            if (pid < 0)
            {
                LOG(ERROR) << "运行时创建子进程失败" << std::endl;
                return -2; // 表示创建子进程失败
            }
            else if (pid == 0)
            {
                // child
                // 资源限制
                SetProLimit(cpu_time, mem_size);

                // 文件重定向
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);

                // 程序替换，运行目标程序
                execl(_excute.c_str(), _excute.c_str(), nullptr);

                LOG(ERROR) << "程序替换失败" << std::endl;
                exit(1);
            }
            else
            {
                // father
                int status = 0; // 接受子进程退出信息
                waitpid(pid, &status, 0);
                // 获取子进程退出时的信号，如果是0，则是正常运行完毕
                // TODO:当程序因为预设的内存不足导致动态库加载失败时，程序返回的还是0，但是在stderr中有加载失败的信息
                int sig = status & 0x7F;
                LOG(INFO) << "运行完毕，info:" << sig << std::endl;
                return sig;
            }
        }
    };
};